﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;

namespace CTC
{
    /// <summary>
    /// 数据包
    /// </summary>
    public class DataPacket
    {
        /**
         * 包头	       2字节	         固定0xABCD
         * P位	       1字节	         默认从0开始,用于1块主板多个P位,此场景游戏机主板根据此字段判断哪个卡头来的通讯,卡头无需理会此字段,由CTC HUB(协议接线器)来自动处理.
         * 命令标识	   2字节	         具体功能的标识00开头为为行业标准协指令,非行业标准的私有指令不要使用00开头
         * 事务ID	   4字节	         用于防止重发
         * 长度	       1字节             数据字段的长,考虑单片机内存资源的消耗,只最大数据长长度限制242,也就是最大帧长度256
         * 数据        n字节,n=长度	     功能携带的参数
         * 校验        4字节	         CRC32国际标准算法,对以上所有数据进行计算(不包含校验字段),算法代码见本仓库源码
         */
        #region 属性
        /// <summary>
        /// 数据包头
        /// </summary>
        public readonly static byte[] DATA_HEAD = new byte[] { 0xAB, 0xCD };

        /// <summary>
        /// 数据包空包长度
        /// </summary>
        public const int PACKET_LENGHT = 14;

        /// <summary>
        /// P位
        /// </summary>
        public byte Player
        {
            private set;
            get;
        }

        /// <summary>
        /// 数据包长度
        /// </summary>
        public byte DataLenght
        {
            get
            {
                if (Data == null) return (byte)0;
                else return (byte)Data.Length;
            }
        }

        /// <summary>
        /// 事务编号
        /// </summary>
        public UInt32 TransactionId
        {
            private set;
            get;
        }
        /// <summary>
        /// 指令
        /// </summary>
        public byte[] Inst
        {
            private set;
            get;
        }

        /// <summary>
        /// 数据包CRC校验
        /// </summary>
        public byte[] PacketCRC
        {
            private set;
            get;
        }

        /// <summary>
        /// 数据体
        /// </summary>
        public byte[] Data
        {
            private set;
            get;
        }
        #endregion

        #region 方法
        /// <summary>
        /// 构造，生成一个数据包，用来发送
        /// </summary>
        /// <param name="player">P位</param>
        /// <param name="TransactionId">包编号</param>
        /// <param name="lost">是否验证丢失</param>
        /// <param name="verify">是否验证数据体</param>
        /// <param name="data">数据体</param>
        public DataPacket(byte player, byte[] isnt, UInt32 TransactionId, params byte[] data)
        {
            this.Player = player;
            this.TransactionId = TransactionId;
            this.Inst = isnt;
            this.Data = data;
            this.PacketCRC = CRC.Uint32ToBytes(CRC.CRC32(GetVeryftPakcetData()));
        }

        /// <summary>
        /// 返回此包需要校验的数据,
        /// </summary>
        /// <returns></returns>
        private byte[] GetVeryftPakcetData()
        {
            byte[] result = new byte[10 + DataLenght];
            Array.Copy(DataPacket.DATA_HEAD, 0, result, 0, DataPacket.DATA_HEAD.Length);//0-1
            result[2] = Player;//2
            Array.Copy(Inst, 0, result, 3, Inst.Length);//3-4
            Array.Copy(CRC.Uint32ToBytes(TransactionId), 0, result, 5, 4);//5-8
            result[9] = DataLenght;//9
            if (Data != null)
            {
                Array.Copy(Data, 0, result, 10, DataLenght);//10-N
            }
            return result;
        }
        /// <summary>
        /// 将数据包输出为数组
        /// </summary>
        /// <returns></returns>
        public byte[] ToArray()
        {
            byte[] sendData = new byte[PACKET_LENGHT + DataLenght];
            Array.Copy(DataPacket.DATA_HEAD, 0, sendData, 0, DataPacket.DATA_HEAD.Length);//0-1
            sendData[2] = Player;//2
            Array.Copy(Inst, 0, sendData, 3, Inst.Length);//3-4
            Array.Copy(CRC.Uint32ToBytes(TransactionId), 0, sendData, 5, 4);//5-8
            sendData[9] = DataLenght;//9
            if (Data != null)
            {
                Array.Copy(Data, 0, sendData, 10, DataLenght);//10-N
            }
            Array.Copy(PacketCRC, 0, sendData, 10 + DataLenght, 4);//10+N
            return sendData;
        }

        /// <summary>
        /// 根据数据格式化一个包,如果数据长度小于最小返回null
        /// </summary>
        /// <param name="data">数据</param>
        /// <returns>失败返回null</returns>
        public static DataPacket Format(byte[] data)
        {
            byte[] da = new byte[data[9]];
            Array.Copy(data, 10, da, 0, da.Length);
            // byte[] data=
            return new DataPacket(data[2], new byte[] { data[3], data[4] }, CRC.BytesToUint32(data[5], data[6], data[7], data[8]), da);
        }

        #endregion
    }

}
